/*
 * Copyright (c) 2023 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <gtest/gtest.h>

#include "dyn_services_card_channel_inner.h"
#include "tee_internal_se_api_mock.h"

namespace OHOS {
namespace SeBaseServices {
namespace UnitTest {
using namespace testing;

TEST(ServiceSeCardChannelTest, CreateSecureElementChannelCaseNullInput)
{
    {
        CardChannel *channel = CreateSecureElementChannel(nullptr, nullptr);
        EXPECT_EQ(channel, nullptr);
    }
    {
        const char *reader = "card0";
        CardChannel *channel = CreateSecureElementChannel(reader, nullptr);
        EXPECT_EQ(channel, nullptr);
    }
}

TEST(ServiceSeCardChannelTest, CreateSecureElementChannelCaseReadNameTooLong)
{
    const char *reader =
        "cardNameIsToooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo"
        "oooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooMuchLength";
    uint8_t aid[] = {1, 2, 3, 4, 5, 6};
    AppIdentifier ident = {.aid = aid, .aidLen = sizeof(aid)};

    CardChannel *channel = CreateSecureElementChannel(reader, &ident);
    EXPECT_EQ(channel, nullptr);
}

TEST(ServiceSeCardChannelTest, CreateSecureElementChannelCaseDriverOpenChannelError)
{
    const char *reader = "card0";
    uint8_t aid[] = {1, 2, 3, 4, 5, 6};
    AppIdentifier ident = {.aid = aid, .aidLen = sizeof(aid)};
    CardChannel *channel = CreateSecureElementChannel(reader, &ident);
    ASSERT_NE(channel, nullptr);

    TeeInternalSeApiMock mock;

    // default mock will open err
    EXPECT_CALL(mock, TEE_SEServiceOpen).Times(Exactly(1)).WillOnce(Return(ERR_GENERIC_ERR));

    ResultCode ret = ChannelOpenChannel(channel);
    EXPECT_EQ(ret, (ERR_GENERIC_ERR | (CHN_OPEN_ERR << 16U)));

    DestroySecureElementChannel(channel);
}

TEST(ServiceSeCardChannelTest, CreateSecureElementChannelCaseNoExistedReader)
{
    const char *reader = "card0";
    uint8_t aid[] = {1, 2, 3, 4, 5, 6};
    AppIdentifier ident = {.aid = aid, .aidLen = sizeof(aid)};
    CardChannel *channel = CreateSecureElementChannel(reader, &ident);
    ASSERT_NE(channel, nullptr);

    TeeInternalSeApiMock mock({{"ese1", {}}, {"ese2", {}}, {"ese3", {}}, {"ese4", {}}});
    {
        InSequence seq;
        EXPECT_CALL(mock, TEE_SEServiceOpen).Times(Exactly(1));
        EXPECT_CALL(mock, TEE_SEServiceGetReaders).Times(Exactly(1));
        EXPECT_CALL(mock, TEE_SEReaderGetName).Times(Exactly(4));
        EXPECT_CALL(mock, TEE_SEServiceClose).Times(Exactly(1));
    }

    ResultCode ret = ChannelOpenChannel(channel);
    EXPECT_EQ(ret >> 16U, CHN_OPEN_ERR);

    DestroySecureElementChannel(channel);
}

TEST(ServiceSeCardChannelTest, CreateSecureElementChannelCaseAppletNotInstalled)
{
    const char *reader = "card0";
    uint8_t aid[] = {1, 2, 3, 4, 5, 6};
    AppIdentifier ident = {.aid = aid, .aidLen = sizeof(aid)};
    CardChannel *channel = CreateSecureElementChannel(reader, &ident);
    ASSERT_NE(channel, nullptr);

    TeeInternalSeApiMock mock({{"ese1", {}}, {"ese2", {}}, {"card0", {}}, {"ese4", {}}});
    {
        InSequence seq;
        EXPECT_CALL(mock, TEE_SEServiceOpen).Times(Exactly(1));
        EXPECT_CALL(mock, TEE_SEServiceGetReaders).Times(Exactly(1));
        EXPECT_CALL(mock, TEE_SEReaderGetName).Times(Exactly(3));
        EXPECT_CALL(mock, TEE_SEReaderOpenSession).Times(Exactly(1));
        EXPECT_CALL(mock, TEE_SESessionOpenLogicalChannel).Times(Exactly(1)); // will open failed
        EXPECT_CALL(mock, TEE_SESessionClose).Times(Exactly(1));
        EXPECT_CALL(mock, TEE_SEReaderCloseSessions).Times(Exactly(1));
        EXPECT_CALL(mock, TEE_SEServiceClose).Times(Exactly(1));
    }

    ResultCode ret = ChannelOpenChannel(channel);
    EXPECT_EQ(ret >> 16U, CHN_OPEN_ERR);
    DestroySecureElementChannel(channel);
}

TEST(ServiceSeCardChannelTest, CreateSecureElementChannelCaseAppletExisted)
{
    const char *reader = "card0";
    uint8_t aid[] = {1, 2, 3, 4, 5, 6};
    AppIdentifier ident = {.aid = aid, .aidLen = sizeof(aid)};
    CardChannel *channel = CreateSecureElementChannel(reader, &ident);
    ASSERT_NE(channel, nullptr);

    {
        TeeInternalSeApiMock mock({
            {"ese1", {}},
            {"ese2", {}},
            {"card0", {std::vector<uint8_t>(aid, aid + sizeof(aid))}},
            {"ese4", {}},
        });
        {
            InSequence seq;
            EXPECT_CALL(mock, TEE_SEServiceOpen).Times(Exactly(1));
            EXPECT_CALL(mock, TEE_SEServiceGetReaders).Times(Exactly(1));
            EXPECT_CALL(mock, TEE_SEReaderGetName).Times(Exactly(3));
            EXPECT_CALL(mock, TEE_SEReaderOpenSession).Times(Exactly(1));
            EXPECT_CALL(mock, TEE_SESessionOpenLogicalChannel).Times(Exactly(1));
            EXPECT_CALL(mock, TEE_SEChannelGetSelectResponse).Times(Exactly(1));
            EXPECT_CALL(mock, TEE_SEChannelClose).Times(Exactly(1));
            EXPECT_CALL(mock, TEE_SESessionClose).Times(Exactly(1));
            EXPECT_CALL(mock, TEE_SEReaderCloseSessions).Times(Exactly(1));
            EXPECT_CALL(mock, TEE_SEServiceClose).Times(Exactly(1));
        }

        ResultCode ret = ChannelOpenChannel(channel);
        ret = ChannelCloseChannel(channel);
        EXPECT_EQ(ret, SUCCESS);
    }

    DestroySecureElementChannel(channel);
}

TEST(ServiceSeCardChannelTest, CreateSecureElementChannelAndTransmitWithRetry)
{
    const char *reader = "card0";
    uint8_t aid[] = {1, 2, 3, 4, 5, 6};
    AppIdentifier ident = {.aid = aid, .aidLen = sizeof(aid)};
    CardChannel *channel = CreateSecureElementChannel(reader, &ident);
    ASSERT_NE(channel, nullptr);
    {
        TeeInternalSeApiMock mock({
            {"ese1", {}},
            {"ese2", {}},
            {"card0", {std::vector<uint8_t>(aid, aid + sizeof(aid))}},
            {"ese4", {}},
        });
        {
            // InSequence seq;
            EXPECT_CALL(mock, TEE_SEServiceOpen).Times(Exactly(4));
            EXPECT_CALL(mock, TEE_SEServiceGetReaders).Times(Exactly(4));
            EXPECT_CALL(mock, TEE_SEReaderGetName).Times(Exactly(3 * 4));
            EXPECT_CALL(mock, TEE_SEReaderOpenSession).Times(Exactly(4));
            EXPECT_CALL(mock, TEE_SESessionOpenLogicalChannel).Times(Exactly(4));
            EXPECT_CALL(mock, TEE_SEChannelGetSelectResponse).Times(Exactly(4));
            EXPECT_CALL(mock, TEE_SEChannelTransmit)
                .Times(Exactly(4)) // failed 3 times, and the 4th success
                .WillOnce(Return(ERR_GENERIC_ERR))
                .WillOnce(Return(ERR_GENERIC_ERR))
                .WillOnce(Return(ERR_GENERIC_ERR))
                .WillOnce(Return(SUCCESS));
            EXPECT_CALL(mock, TEE_SEChannelClose).Times(Exactly(4));
            EXPECT_CALL(mock, TEE_SESessionClose).Times(Exactly(4));
            EXPECT_CALL(mock, TEE_SEReaderCloseSessions).Times(Exactly(4));
            EXPECT_CALL(mock, TEE_SEServiceClose).Times(Exactly(4));
        }

        ApduCommandHeader header = {0x00, 0xa4, 0x04, 0x00};
        const uint8_t data[] = {0x00, 0x01, 0x02, 0x03};
        CommandApdu *cmd = CreateCommandApdu(&header, data, sizeof(data), MAX_APDU_RESP_SIZE);
        ASSERT_NE(cmd, nullptr);

        ResponseApdu *rsp = nullptr;
        ResultCode ret = ChannelOpenTransmitCloseWithRetry(channel, 4, nullptr, cmd, &rsp);
        EXPECT_EQ(ret, SUCCESS);
        DestroyCommandApdu(cmd);
        DestroyResponseApdu(rsp);
    }

    DestroySecureElementChannel(channel);
}

} // namespace UnitTest
} // namespace SeBaseServices
} // namespace OHOS